using System.Collections;
using System.Collections.Generic;
using System.IO;
using Google.Protobuf;
using System.Net.Sockets.Kcp.Simple;
using System.Threading.Tasks;
using System.Buffers;
using System;

public class NetMsgPacker
{
    byte[] outputBuffer;
    MemoryStream memoryStream;
    CodedOutputStream outputStream;
    BinaryWriter binaryWriter;
    CodedInputStream inputStream;

    IKcpClient kcpClient;
    Queue<IMessage> queuedMsg;
    int errorCode = 0;
    public bool IsError => errorCode < 0;
    public int ErrorCode => errorCode;
    bool started;

    async void AsyncSend()
    {
        if (!started)
        {
            while (queuedMsg.Count > 0)
            {
                MessagePool.Instance.Recycle(queuedMsg.Dequeue());
            }
            return;
        }
        while(queuedMsg.Count > 0)
        {
            var msg = queuedMsg.Peek();
            int res = Send(msg);
            if (res >= 0)
            {
                MessagePool.Instance.Recycle(queuedMsg.Dequeue());
                continue;
            }

            if(res == -2)
            {
                break;
            }

            errorCode = res;
            return;
        }
        await Task.Delay(10);
        AsyncSend();
    }

    public NetMsgPacker(IKcpClient kcpClient,int capacity = 1024)
    {
        started = true;
        this.kcpClient = kcpClient;
        outputBuffer = new byte[capacity];
        outputStream = new CodedOutputStream(outputBuffer);
        inputStream  = new CodedInputStream(Array.Empty<byte>());
        memoryStream = new MemoryStream(outputBuffer);
        binaryWriter = new BinaryWriter(memoryStream);
        queuedMsg = new Queue<IMessage>();
        AsyncSend();
    }

    public void HandleRecieve(IMemoryOwner<byte> memoryOwner, int length)
    {
        if (memoryOwner is IRawMemoryOwner pooledOwner)
        {
            int msgLength = pooledOwner.Raw.getMsgLength();
            int msgId = pooledOwner.Raw.getMsgId();
            inputStream.Reset(pooledOwner.Raw, 8, length);
            if (msgLength > length)
                return;

            if (!MessageManager.TryGetMsgType(msgId, out var msgType))
                return;

            var msg = MessagePool.Instance.Fetch(msgType);
            msg.MergeFrom(inputStream);
            NetMsgQueue.Enqueue(msg);
        }
    }

    public void Dispose()
    {
        started = false;
    }

    public void SendMsg(IMessage msg)
    {
        queuedMsg.Enqueue(msg);
    }

    int Send(IMessage msg)
    {
        if (MessageManager.TryGetMsgId(msg.GetType(),out int msgId))
        {
            outputBuffer.putMsgId(msgId);
            outputStream.Reset(this.outputBuffer, 8, this.outputBuffer.Length - 8);
            msg.WriteTo(outputStream);
            int msgLength = (int)outputStream.Position;
            outputBuffer.putMsgLength(msgLength);
            return kcpClient.Send(outputBuffer, msgLength);
        }
        return -111;
    }
}
